﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace KINLO.DRV.Machine
{
    /// <summary>
    /// 0x03读多个寄存器     0x06 写单个寄存器  0x10写多个寄存器
    /// </summary>
    public class ModbusTcp
    {
        public int ID { get; set; }
        public Byte SlaveID { get; set; }
        public string IP { get; set; }
        public int PORT { get; set; }
        public bool IsConnected { get; private set; }
        Socket socket;

        public static ModbusTcp CreateIP(int id, string ip, byte slaveId = 0x01)
        {
            var vals = ip.Split(':');
            var _ip = vals[0].Trim();
            var _port = 502;
            if (vals.Length > 1)
                if (!int.TryParse(vals[1], out _port))
                {
                    _port = 502;
                }
            return new ModbusTcp(id, _ip, _port, slaveId);
        }

        public ModbusTcp(int id,string ip,int port, byte slaveId = 0x01)
        {
            this.ID = id;
            this.IP = ip;
            this.PORT = port;
            this.SlaveID = slaveId;
        }
        bool Open()
        {
            try
            {
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                socket.ReceiveTimeout = 2000;
                socket.SendTimeout = 2000;
                socket.Connect(IP, PORT);
                return IsConnected = true;
            }
            catch (Exception)
            {
                socket.Close();
                return IsConnected = false;
            }
        }
        MemoryStream HeaderMemoryStream()
        {
            var ms = new MemoryStream();
            ms.WriteByte(0x0);//事务标识高位 Hi
            ms.WriteByte(0x0);//事务标识低位 Lo
            ms.WriteByte(0x0);
            ms.WriteByte(0x0);
            return ms;
        }
        byte[] HeightLowByteConvert(ushort val)
        {
            var h = (byte)((val & 0xff00) >> 8);
            var l = (byte)(val & 0x00ff);
            return new byte[] { h, l };
        }
        object lockerFloat=new object();
        /// <summary>
        /// 读加压杠浮点数据
        /// </summary>
        /// <param name="startAddress"></param>
        /// <param name="numberOfPoints"></param>
        /// <returns></returns>
        public double[] ReadPressureCharFloat(ushort startAddress, ushort numberOfPoints)
        {
            try
            {
                if (!IsConnected)
                    Open();
                if (!IsConnected)
                    return null;
                Console.WriteLine("开始读取数据");
                var ms = HeaderMemoryStream();
                var FunctionCode = (byte)FunctionType.ReadHoldingRegisters;  //功能码
                var startAddressBytes = HeightLowByteConvert(startAddress); //开始地址
                var numberOfPointsBytes = HeightLowByteConvert(numberOfPoints); //数量

                var bytes = new List<byte>();
                bytes.Add(SlaveID);     //服务端ID
                bytes.Add(FunctionCode);
                bytes.AddRange(startAddressBytes);
                bytes.AddRange(numberOfPointsBytes);
                var lenByte = HeightLowByteConvert((ushort)bytes.Count);
                bytes.InsertRange(0, lenByte);
                var dataBytes = bytes.ToArray();
                ms.Write(dataBytes, 0, dataBytes.Length);
                var data = ms.ToArray();

                List<byte> recvBytes = new List<byte>();
                lock (locker)
                {
                    var sendLen = socket.Send(data);
                    var buff = new byte[1024];
                    var recvLen = socket.Receive(buff);
                    recvBytes = buff.Take(recvLen).ToList();
                    IsConnected = recvLen > 0 ? true : false;
                }
                if (recvBytes.Count() >= 8)
                {
                    var code = recvBytes.Skip(7).Take(1).FirstOrDefault();
                    if (code > 0x80) return null;

                    var byteLen = recvBytes.Skip(8).Take(1).FirstOrDefault();
                    var takeByte = recvBytes.Skip(9).Take(byteLen);
                    var hexValue = takeByte.Select(s => s.ToString("X2")).ToList();
                    var results = new List<double>(); // 使用 float 数组来存储浮点数结果

                    for (var i = 0; i < byteLen / 4; i++) // 每个浮点数占4个字节
                    {
                        var bytesForFloat = takeByte.Skip(i * 4).Take(4).ToArray(); // 取4个字节来表示一个浮点数
                        double floatValue = ConvertBytesToFloat(bytesForFloat); // 调用方法将字节数组转换为浮点数
                        results.Add(floatValue);
                    }

                    return results.Count == numberOfPoints / 2 ? results.ToArray() : null;
                }
                return null;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"ReadHoldingRegisters:{ex}");
                IsConnected = false;
                return null;
            }
        }
        float ConvertBytesToFloat(byte[] data)
        {
            if (data.Length != 4)
            {
                return 0;
            }

            // 互换高低位字节
            byte temp = data[0];
            data[0] = data[1];
            data[1] = temp;

            temp = data[2];
            data[2] = data[3];
            data[3] = temp;

            // 将字节数组转换为浮点数
            return BitConverter.ToSingle(data, 0);
        }

        object locker = new object();
        public ushort[] ReadHoldingRegisters(ushort startAddress, ushort numberOfPoints)
        {
            try
            {
                if (!IsConnected)
                    Open();
                if (!IsConnected)
                    return null;

                var ms = HeaderMemoryStream();
                var FunctionCode = (byte)FunctionType.ReadHoldingRegisters;  //功能码
                var startAddressBytes = HeightLowByteConvert(startAddress); //开始地址
                var numberOfPointsBytes = HeightLowByteConvert(numberOfPoints); //数量

                var bytes = new List<byte>();
                bytes.Add(SlaveID);     //服务端ID
                bytes.Add(FunctionCode);
                bytes.AddRange(startAddressBytes);
                bytes.AddRange(numberOfPointsBytes);
                var lenByte = HeightLowByteConvert((ushort)bytes.Count);
                bytes.InsertRange(0, lenByte);
                var dataBytes = bytes.ToArray();
                ms.Write(dataBytes, 0, dataBytes.Length);
                var data = ms.ToArray();

                List<byte> recvBytes = new List<byte>();
                lock (locker)
                {
                    var sendLen = socket.Send(data);
                    var buff = new byte[1024];
                    var recvLen = socket.Receive(buff);
                    recvBytes = buff.Take(recvLen).ToList();
                    IsConnected = recvLen > 0 ? true : false;
                }
                if (recvBytes.Count() >= 8)
                {
                    var code = recvBytes.Skip(7).Take(1).FirstOrDefault();
                    if (code > 0x80) return null;

                    var byteLen = recvBytes.Skip(8).Take(1).FirstOrDefault();
                    var takeByte = recvBytes.Skip(9).Take(byteLen);
                    var hexValue = takeByte.Select(s => s.ToString("X2")).ToList();
                    var results = new List<UInt16>();
                    for (var i = 0; i < byteLen / 2; i++)
                    {
                        var v1 = hexValue.Skip(i * 2).Take(2).ToList();
                        var v2 = string.Join("", v1);
                        var v3 = UInt16.Parse(v2, System.Globalization.NumberStyles.HexNumber);
                        results.Add(v3);
                    }
                    return results.Count == numberOfPoints ? results.ToArray() : null;
                }
                return null;
            }
            catch(Exception ex)
            {
                Console.WriteLine($"ReadHoldingRegisters:{ex}");
                IsConnected = false;
                return null;
            }            
        }

       public bool WriteSingleRegister(ushort startAddress, ushort value)
        {
            try
            {
                if (!IsConnected)
                    Open();
                if (!IsConnected)
                    return false;

                var ms = HeaderMemoryStream();
                var FunctionCode = (byte)FunctionType.WriteSingleRegister;  //功能码
                var startAddressBytes = HeightLowByteConvert(startAddress); //开始地址
                var valueBytes = HeightLowByteConvert(value); //数量 

                var bytes = new List<byte>();
                bytes.Add(SlaveID);     //服务端ID
                bytes.Add(FunctionCode);
                bytes.AddRange(startAddressBytes);
                bytes.AddRange(valueBytes);
                var lenByte = HeightLowByteConvert((ushort)bytes.Count);
                bytes.InsertRange(0, lenByte);
                var dataBytes = bytes.ToArray();
                ms.Write(dataBytes, 0, dataBytes.Length);
                var data = ms.ToArray();
                List<byte> recvBytes = new List<byte>();
                lock (locker)
                {
                    var sendLen = socket.Send(data);
                    var buff = new byte[1024];
                    var recvLen = socket.Receive(buff);
                    recvBytes = buff.Take(recvLen).ToList();

                    IsConnected = recvLen > 0 ? true : false;
                }
                if (recvBytes.Count() >= 8)
                {
                    var code = recvBytes.Skip(7).Take(1).FirstOrDefault();
                    return code > 0x80 ? false : true;
                }
                return false;
            }
            catch(Exception ex)
            {
                Console.WriteLine($"WriteSingleRegister:{ex}");
                return IsConnected = false;
            }            
        }
        public bool WriteMultipleRegisters(ushort startAddress, ushort[] datas)
        {
            try
            {
                if (!IsConnected)
                    Open();
                if (!IsConnected)
                    return false;

                var ms = HeaderMemoryStream();
                var FunctionCode = (byte)FunctionType.WriteMultipleRegisters;  //功能码
                var startAddressBytes = HeightLowByteConvert(startAddress); //开始地址
                var registerCount = HeightLowByteConvert((ushort)datas.Length); //寄存器个数
                var dataByte = new List<byte>();
                foreach (var item in datas)
                {
                    var valueBytes = HeightLowByteConvert(item);
                    dataByte.AddRange(valueBytes);
                }
                var valueByteCount = Convert.ToByte(dataByte.Count);//字节个数
                var bytes = new List<byte>();
                bytes.Add(SlaveID);     //服务端ID
                bytes.Add(FunctionCode);
                bytes.AddRange(startAddressBytes);
                bytes.AddRange(registerCount);
                bytes.Add(valueByteCount);
                bytes.AddRange(dataByte);
                var lenByte = HeightLowByteConvert((ushort)bytes.Count);
                bytes.InsertRange(0, lenByte);
                var dataBytes = bytes.ToArray();
                ms.Write(dataBytes, 0, dataBytes.Length);
                var data = ms.ToArray();

                List<byte> recvBytes = new List<byte>();
                lock (locker)
                {
                    var sendLen = socket.Send(data);
                    var buff = new byte[1024];
                    var recvLen = socket.Receive(buff);
                    recvBytes = buff.Take(recvLen).ToList();
                    IsConnected = recvLen > 0 ? true : false;
                }
                if (recvBytes.Count() >= 8)
                {
                    var code = recvBytes.Skip(7).Take(1).FirstOrDefault();
                    if (code > 0x80)
                        Console.WriteLine($"Fail {string.Join(";", recvBytes)}");
                    return code > 0x80 ? false : true;
                }
                return false;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"WriteMultipleRegisters:{ex}");
                return IsConnected = false; 
            }           
        }
    }
    /// <summary>
    /// 功能键
    /// </summary>
    public enum FunctionType:byte
    {
        ReadHoldingRegisters = 0x03,
        WriteSingleRegister = 0x06,
        WriteMultipleRegisters = 0x10,
    }
}
 